# -*- coding: utf-8 -*-
# created on 2017/3/1

import ast
import sympy
from sympy import acos
from mathsolver.functions.base import *
from mathsolver.functions.base.base import BaseFunction
from mathsolver.functions.daoshu.daoshuqiuzhi import HanShuDaoShuQiuZhi
from mathsolver.functions.hanshu.compute import HanShuCompute


class ProcessExpression(BaseFunction):
    @staticmethod
    def norm(obj):
        if len(obj.value) == 2:
            f1, f2 = obj.sympify()
            return sqrt(f1 ** 2 + f2 ** 2)
        else:
            f1, f2, f3 = obj.sympify()
            return sqrt(f1 ** 2 + f2 ** 2 + f3 ** 2)

    def process(self, node):
        if isinstance(node, ast.Num):
            return str(node.n)
        elif isinstance(node, ast.Name):
            return str(node.id)
        elif isinstance(node, ast.UnaryOp):
            value = self.process(node.operand)
            if isinstance(value, BaseVector):
                return value * (-1)
            else:
                return "-" + self.process(node.operand)
        elif isinstance(node, ast.Str):
            return node.s
        elif isinstance(node, ast.Expr):
            return self.process(node.value)
        elif isinstance(node, ast.BinOp):
            left = self.process(node.left)
            right = self.process(node.right)
            if isinstance(left, (str, int)) and isinstance(right, (str, int)):
                if isinstance(node.op, ast.Add):
                    return "(%s)+(%s)" % (left, right)
                if isinstance(node.op, ast.Sub):
                    return "(%s)-(%s)" % (left, right)
                elif isinstance(node.op, ast.Mult):
                    return "(%s)*(%s)" % (left, right)
                elif isinstance(node.op, ast.Pow):
                    return "(%s)**(%s)" % (left, right)
                elif isinstance(node.op, ast.Div):
                    return "(%s)/(%s)" % (left, right)
            elif isinstance(left, BaseVector) and isinstance(right, BaseVector):
                if isinstance(node.op, ast.Add):
                    return left + right
                if isinstance(node.op, ast.Sub):
                    return left - right
                elif isinstance(node.op, ast.Mult):
                    return left * right
            elif isinstance(left, (str, int, Symbol)) and isinstance(right, BaseVector):
                if isinstance(node.op, ast.Mult):
                    return right * left
            elif isinstance(left, BaseVector) and isinstance(right, (str, int)):
                if isinstance(node.op, ast.Div):
                    return left / right

        elif isinstance(node, ast.Compare):
            left = self.process(node.left)
            right = self.process(node.comparators[0])
            if isinstance(node.ops[0], ast.GtE):
                ops = ">="
            elif isinstance(node.ops[0], ast.Gt):
                ops = ">"
            elif isinstance(node.ops[0], ast.Lt):
                ops = "<"
            elif isinstance(node.ops[0], ast.LtE):
                ops = "<="
            return BaseIneq([left, ops, right])
        elif isinstance(node, ast.Call):
            func = node.func.id
            args = [self.process(arg) for arg in node.args]
            # known_func = self.search(func)
            if func == "f" or func == "g" or self.search(func):
                stepsolver = HanShuCompute(known=self.known).solver(BaseFuncName({'var': args[0], 'type': '', 'name': str(func)}))
                self.append(stepsolver, False)
                value = stepsolver.output[0]
                assert value is not None
                if isinstance(value, BaseValue):
                    value = str(value.value)
                return value
            elif func == "distance":
                p1 = self.search(args[0]).sympify()
                p2 = self.search(args[1]).sympify()
                self.label.add("距离公式")
                return str(sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2))
            elif "_d" in func and self.search(func.replace("_d", "")):
                func = self.search(func.replace("_d", ""))
                stepsolver = HanShuDaoShuQiuZhi().solver(BaseFuncName({"name": "", "type": "", "var": args[0]}), func)
                return str(stepsolver.output[0].value)
            elif func == "Abs" and isinstance(args[0], BaseVector):
                return str(self.norm(args[0]))
            elif func == "Angle":
                p1, p2, p3 = [self.search(nn).sympify() for nn in args]
                a = (p2[0] - p1[0], p2[1] - p1[1])
                b = (p2[0] - p3[0], p2[1] - p3[1])
                t1 = a[0] * b[0] + a[1] * b[1]
                t2 = sqrt((a[0] ** 2 + a[1] ** 2) * (b[0] ** 2 + b[1] ** 2))
                return str(acos(t1 / t2))
            elif func == "d":
                return "%s/180*pi" % str(args[0])
            elif func == "line":
                return args
            elif hasattr(sympy, func):
                return "%s(%s)" % (func, ",".join(args))  # str(getattr(sympy,func)(*[sympify(arg) for arg in args]))
            elif func == "vector":
                if args[0] == 0 or args[0] == str('0'):
                    return BaseVector([0, 0])
                else:
                    if len(args[0]) == 2 and self.search(args[0][0]) and self.search(args[0][1]):
                        n1, n2 = args[0]
                        p1 = self.search(n1).sympify()
                        p2 = self.search(n2).sympify()
                        return BaseVector([p2[0] - p1[0], p2[1] - p1[1]])
                    elif len(args[0]) == 1 and args[0][0] == '0':
                        return BaseVector([0, 0])
                    else:
                        name = sympify("%s(%s)" % (func, args[0]))
                        name = name.args[0]
                        name = str('Axis_') + str(name)
                        return BaseVector(self.known[name])

            elif func == "interangle":
                v1, v2 = args
                return str(sympify(v1 * v2) / (self.norm(v1) - self.norm(v2)))

        elif isinstance(node, ast.Attribute):
            attr = node.attr
            value = node.value
            if isinstance(value, ast.Call):  # f(-1).d
                func = value.func.id
                args = [self.process(arg) for arg in value.args]
                known_func = self.search(func)
                method = getattr(known_func, attr)
                stepsolver = method(*args)
                self.append(stepsolver, False)
                value = stepsolver.output[0]
                if isinstance(value, BaseValue):
                    value = value.sympify()
                return value
            else:
                obj = self.known[self.process(node.value)]
                stepsolver = getattr(obj, node.attr)()
                self.append(stepsolver, False)
                return stepsolver.output[0]
        else:
            raise ValueError()

    def solver(self, *args):
        expr = args[0].value
        if isinstance(args[0], BaseVector):
            self.steps.append(["计算%s得" % expr, ""])
        else:
            self.steps.append(["计算%s得" % args[0].printing(), ""])
        if "f(" in expr and isinstance(self.search("f"), BasePieceFunc):
            from mathsolver.functions.hanshu.fenduan_jiebudengshi import FenDuanHanShuJieBuDengShi
            for op in [">=", "<=", "!=", ">", "<"]:
                if op in expr:
                    item = expr.split(op)
                    ineq = BaseIneq([item[0], op, item[1]])
                    break
            return FenDuanHanShuJieBuDengShi(known=self.known).solver(self.search("f"), ineq)

        # self.steps.append(["计算%s得" % args[0].printing(),""])
        if isinstance(expr, (list, tuple)):
            if len(expr) == 2:
                trees = [ast.parse(str(v)).body[0] for v in expr]
                self.output.append(BaseEq([self.process(tree) for tree in trees]))
            elif len(expr) == 3:
                f1 = self.process(ast.parse(expr[0]).body[0])
                f2 = self.process(ast.parse(expr[2]).body[0])
                self.output.append(BaseIneq([f1, expr[1], f2]))
            else:
                raise ValueError()
        elif isinstance(expr, str):
            tree = ast.parse(expr).body[0]
            output = self.process(tree)
            if isinstance(output, str):
                self.output.append(BasePoly(output))
            else:
                self.output.append(output)
        else:
            raise ValueError()

        self.steps.append(["", self.output[0].printing()])
        return self


class ProcessExpressions(BaseFunction):
    def solver(self, *args):
        exprs = args[0].objs
        outputs = []
        for expr in exprs:
            output = ProcessExpression(known=self.known).solver(expr).output[0]
            outputs.append(output)

        if all([isinstance(obj, BaseEq) for obj in outputs]):
            obj = BaseEqs([obj.value for obj in outputs])
        else:
            obj = BaseMultiple({"objs": outputs, "type": "poly"})

        self.output.append(obj)
        return self


if __name__ == '__main__':
    ProcessExpression(known={sympify("vector(a)"): [1, -2], sympify("vector(b)"): [-3, 4], sympify("vector(c)"): [3, 2]}).solver(
        BaseValue("(vector(a)+2*vector(b))*vector(c)"))
    pass
